#ifndef PROCSI_H_
#define PROCSI_H_

#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

// PVM : Procsi Virtual Machine
/*!< Taille de la mémoire de la PVM */
#define MEMSIZE 128

/*!<  Indice du Program Counter (PC) au démarrage */
#define PC_START 0

/*!<  Indice du State Register (SR) au démarrage*/
#define SR_START 0

/*!<  Indice du Stack Pointer (SP) au démarrage*/
#define SP_START MEMSIZE-1

/*!<  Nombre de registres du PVM*/
#define NREGS 8

/*!<  Incrémentation de la pile se faisant dans le sens inverse de la mémoire*/
#define SP_INCR -1

/*!<  Définir la taille des registres ("MOT")*/
typedef uint16_t REG;

/*!<  Définir les valeurs numérique des registres PC, SP et SR*/
#define PC 100
#define SP 101
#define SR 102

typedef union
{
	REG brut;
	struct
	{
		unsigned codeop : 6;
		unsigned mode   : 4;
		unsigned source : 3;
		unsigned dest   : 3;
	} codage;
} Mot;

typedef struct {
	REG pc;
	REG sp;
	REG sr;
	REG reg[8];
	Mot *mem;
} PVM;


/**
 * Liste de toutes les instructions disponibles.
 * Les valeurs des élements de l'enum sont des code-op pour une instruction donnée.
 * Certaines valeurs ont été données dans les cours de A.A, les autres arbitrairement.
 */
enum instructions
{
	LOAD  =	0x8,
	STORE =	0x9,
	ADD   =	0x0,
	SUB   =	0x1,
	JMP   =	0x2,
	JEQ   =	0x3,
	CALL  =	0x4,
	RET   =	0x5,
	PUSH  =	0x6,
	POP   =	0x7,
	MOV   =	0xA,
	AND   =	0xB,
	OR    =	0xC,
	SHL   =	0xD,
	SHR   =	0xE,
	HALT  =	0xF
};

typedef enum
{
	REGREG = 0x0, /*!<  Valeur binaire : 0000*/
	REGIMM = 0x4, /*!<  Valeur binaire : 0100*/
	REGDIR = 0x8, /*!<  Valeur binaire : 1000*/
	REGIND = 0xC, /*!<  Valeur binaire : 1100*/
	DIRIMM = 0x5, /*!<  Valeur binaire : 0100*/
	DIRREG = 0x1, /*!<  Valeur binaire : 0001*/
	INDIMM = 0x6, /*!<  Valeur binaire : 0110*/
	INDREG = 0x2, /*!<  Valeur binaire : 0010*/
	REGISTER,
	IMMEDIATE,
	DIRECT,
	INDIRECT
} mode;

PVM* newPVM();
bool step(PVM *pvm);
bool exec(PVM *pvm, Mot *mot);

REG* getDestination(PVM *pvm, Mot *mot);
Mot getSource(PVM *pvm, Mot *mot);

void pvm_status(PVM *pvm);
bool pvm_print_memoire(PVM *pvm, unsigned int mem);
bool pvm_print_registre(PVM *pvm, unsigned int reg);

bool verifierAccesMemoire(REG *index);
bool verifierAccesRegistre(REG index);
#endif
